package utils

import (
	"errors"
	"github.com/golang-jwt/jwt/v5"
	"time"
)

// JWTClaims 自定义声明结构体
type JWTClaims struct {
	UserID   uint   `json:"user_id"`
	Username string `json:"username"`
	Email    string `json:"email"`
	jwt.RegisteredClaims
}

// JWTConfig JWT配置
type JWTConfig struct {
	Secret     string
	ExpireTime time.Duration
}

var (
	// ErrTokenExpired token过期
	ErrTokenExpired = errors.New("token is expired")
	// ErrTokenInvalid token无效
	ErrTokenInvalid = errors.New("token is invalid")
)

// DefaultJWTConfig 默认JWT配置
var DefaultJWTConfig = &JWTConfig{
	Secret:     "zatta-gateway-secret",
	ExpireTime: time.Hour * 24, // 默认24小时过期
}

// GenerateToken 生成JWT token
func GenerateToken(claims *JWTClaims, config *JWTConfig) (string, error) {
	if config == nil {
		config = DefaultJWTConfig
	}

	// 设置过期时间
	if claims.ExpiresAt == nil {
		claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(config.ExpireTime))
	}

	// 设置签发时间
	if claims.IssuedAt == nil {
		claims.IssuedAt = jwt.NewNumericDate(time.Now())
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(config.Secret))
}

// ParseToken 解析JWT token
func ParseToken(tokenString string, config *JWTConfig) (*JWTClaims, error) {
	if config == nil {
		config = DefaultJWTConfig
	}

	token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.Secret), nil
	})

	if err != nil {
		// 检查是否是因为token过期导致的错误
		if errors.Is(err, jwt.ErrTokenExpired) {
			return nil, ErrTokenExpired
		}
		return nil, ErrTokenInvalid
	}

	if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, ErrTokenInvalid
}

// ValidateToken 验证JWT token有效性
func ValidateToken(tokenString string, config *JWTConfig) (bool, error) {
	_, err := ParseToken(tokenString, config)
	if err != nil {
		return false, err
	}
	return true, nil
}

// RefreshToken 刷新JWT token
func RefreshToken(tokenString string, config *JWTConfig) (string, error) {
	claims, err := ParseToken(tokenString, config)
	if err != nil {
		return "", err
	}

	// 创建新的claims，更新过期时间
	newClaims := &JWTClaims{
		UserID:   claims.UserID,
		Username: claims.Username,
		Email:    claims.Email,
		RegisteredClaims: jwt.RegisteredClaims{
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(config.ExpireTime)),
		},
	}

	return GenerateToken(newClaims, config)
}
